/**
*********************************************************************************************************
*               Copyright(c) 2017, Realtek Semiconductor Corporation. All rights reserved.
**********************************************************************************************************
* @file      dataTrans_queue.c
* @brief    This file provides queue for receving data .
* @details
* @author  elliot_chen
* @date     2017-04-27
* @version  v1.0
*********************************************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "asr_rtl8763bo_port.h"

#if USE_LV_BLUETOOTH != 0

#include "dataTrans_queue.h"

/* Internal defines -----------------------------------------------------------*/
#define LOOP_QUEUE_DISCARD_MAX_SIZE         ((uint16_t)256)

/* Globals -------------------------------------------------------------------*/
/* Loop queue management data structure */
LoopQueue_TypeDef   LoopQueue;
/* Loop queue write callback function pointer */
WriteQueueFnCB LoopQueue_WriteFnCB = NULL;

/**
  * @brief  Initializes loop queue to their default reset values.
  * @param  pPacket: point to loop queue data structure.
  * @retval None
  */
static void LoopQueue_Init(LoopQueue_TypeDef * pQueueStruct)
{
    memset(pQueueStruct, 0, sizeof(LoopQueue_TypeDef));
    memset(pQueueStruct->buf, 0xff, LOOP_QUEUE_MAX_SIZE);
}

/**
  * @brief  check loop queue if will overflow or not.
  * @param  pQueueStruct: point to loop queue dta struct.
  * @retval The new state of loop queue (overflow:TRUE, not overflow:FALSE).
  */
static bool LoopQueue_IsOverFlow(LoopQueue_TypeDef * pQueueStruct, uint16_t write_size)
{
    /* Check queue status */
    if(pQueueStruct->writeIndex >= pQueueStruct->readIndex) {
        if((pQueueStruct->writeIndex + write_size) >= (LOOP_QUEUE_MAX_SIZE + pQueueStruct->readIndex)) {
            ///MODLOGD("Loop Queue will OverFlow 1!");
            pQueueStruct->overFlow = true;
            return true;
        }
    } else {
        if((pQueueStruct->writeIndex + write_size) >= pQueueStruct->readIndex) {
            ///MODLOGD("Loop Queue will OverFlow 2!");
            pQueueStruct->overFlow = true;
            return true;
        }
    }
    return false;
}

/**
  * @brief  Get valid data length.
  * @param None.
  * @retval None.
  */
static uint16_t LoopQueue_GetDataLen(LoopQueue_TypeDef * pQueueStruct)
{
    return ((LOOP_QUEUE_MAX_SIZE + pQueueStruct->writeIndex - pQueueStruct->readIndex) &
            QUEUE_CAPABILITY);
}

/**
  * @brief  check loop queue if empty or not.
  * @param  pPacket: point to IR packet struct.
  * @retval The new state of loop queue (empty:true, not empty:false).
  */
static bool LoopQueue_IsEmpty(LoopQueue_TypeDef * pQueueStruct)
{
    return (pQueueStruct->writeIndex == pQueueStruct->readIndex);
}

/**
  * @brief  Write buffer data to loop queue.
  * @param None.
  * @retval status of loop queue:
    TRUE: write success.
    FALSE: write failure.
  */
static QUEUE_WRITE_STAT LoopQueue_WriteBuf(LoopQueue_TypeDef * pQueueStruct, void * peri,
        uint16_t len)
{
    uint16_t tail_len = 0;

    if(LoopQueue_WriteFnCB == NULL) {
        return QUEUE_WRTIE_CB_NULL;
    }

    if(!LoopQueue_IsOverFlow(pQueueStruct, len)) {
        if(pQueueStruct->writeIndex + len <= LOOP_QUEUE_MAX_SIZE) {
            uint16_t act_len;

            act_len = LoopQueue_WriteFnCB(peri, &(pQueueStruct->buf[pQueueStruct->writeIndex]), len);  /// dma: UART_CopyData() ; fifo: UART_ReceiveData()
            pQueueStruct->writeIndex += act_len;

        } else {
            uint16_t act_len1, act_len2;

            tail_len = LOOP_QUEUE_MAX_SIZE - pQueueStruct->writeIndex;
            act_len1 = LoopQueue_WriteFnCB(peri, &(pQueueStruct->buf[pQueueStruct->writeIndex]), tail_len);
            if(act_len1 < tail_len) {
                pQueueStruct->writeIndex += act_len1;
            } else {
                pQueueStruct->writeIndex = 0;
                act_len2 = LoopQueue_WriteFnCB((void *)((uint8_t *)peri + act_len1), &(pQueueStruct->buf[pQueueStruct->writeIndex]), len - act_len1);
                pQueueStruct->writeIndex += act_len2;
            }
        }
        pQueueStruct->writeIndex &= QUEUE_CAPABILITY;
    } else {
        /* Discard data */
        uint8_t discard_buf[LOOP_QUEUE_DISCARD_MAX_SIZE];

        len = (len > LOOP_QUEUE_DISCARD_MAX_SIZE) ? LOOP_QUEUE_DISCARD_MAX_SIZE : len;
        LoopQueue_WriteFnCB(peri, discard_buf, len);
        MODLOGD("Discard bytes: %d! ri:%d wi:%d", len, pQueueStruct->readIndex, pQueueStruct->writeIndex);

        return QUEUE_WRITE_OVERFLOW;
    }

    return QUEUE_WRITE_OK;
}

/**
  * @brief  Read buffer data from loop queue.
  * @param None.
  * @retval None.
  */
static void LoopQueue_ReadBuf(LoopQueue_TypeDef * pQueueStruct, uint8_t * pBuf, uint16_t len)
{
    uint16_t remain = 0;

    /* Check parameters */
    if((len == 0) || (pBuf == NULL)) {
        return;
    }

    if(pQueueStruct->readIndex + len <= LOOP_QUEUE_MAX_SIZE) {
        memcpy(pBuf, &(pQueueStruct->buf[pQueueStruct->readIndex]), len);
        pQueueStruct->readIndex += len;
    } else {
        remain = LOOP_QUEUE_MAX_SIZE - pQueueStruct->readIndex;
        memcpy(pBuf, &(pQueueStruct->buf[pQueueStruct->readIndex]), remain);
        pQueueStruct->readIndex = 0;
        memcpy(pBuf + remain, &(pQueueStruct->buf[pQueueStruct->readIndex]), len - remain);
        pQueueStruct->readIndex = len - remain;
    }
    pQueueStruct->readIndex &= QUEUE_CAPABILITY;
}

/**
  * @brief  copy buffer data from loop queue.
  * @param None.
  * @retval None.
  */
static void LoopQueue_CopyData(LoopQueue_TypeDef * pQueueStruct, uint8_t * pBuf, uint16_t len)
{
    uint16_t readIndex = pQueueStruct->readIndex;
    uint16_t remain = 0;

    /* Check parameters */
    if((len == 0) || (pBuf == NULL)) {
        return;
    }

    if(readIndex + len <= LOOP_QUEUE_MAX_SIZE) {
        memcpy(pBuf, &(pQueueStruct->buf[readIndex]), len);
        readIndex += len;
    } else {
        remain = LOOP_QUEUE_MAX_SIZE - readIndex;
        memcpy(pBuf, &(pQueueStruct->buf[readIndex]), remain);
        readIndex = 0;
        memcpy(pBuf + remain, &(pQueueStruct->buf[readIndex]), len - remain);
    }
    readIndex &= QUEUE_CAPABILITY;
}

/**
 * @brief Register callback function for reading data from peripheral and write to loop queue.
 * @param pFunc callback function.
 * @return none
*/
void DataTrans_RegisterPeriphReadFnCB(WriteQueueFnCB pFunc)
{
    LoopQueue_WriteFnCB = pFunc;
}

/**
  * @brief  Initializes loop queue to their default reset values.
  * @param  None.
  * @retval None.
  */
void DataTrans_InitQueue(void)
{
    LoopQueue_Init(&LoopQueue);
}

/**
  * @brief  Check loop queue is empty or not.
  * @param None.
  * @retval status of loop queue.:
    TRUE: loop queue is empty.
    FALSE: loop queue is not empty.
  */
bool DataTrans_QueueIsEmpty(void)
{
    return LoopQueue_IsEmpty(&LoopQueue);
}

/**
  * @brief  Write buffer data to loop queue.
  * @param None.
 * @retval status of loop queue: QUEUE_WRITE_STAT type
  */
QUEUE_WRITE_STAT DataTrans_WriteQueue(void * peri, uint16_t len)
{
#if 1
    QUEUE_WRITE_STAT stat;

    stat = LoopQueue_WriteBuf(&LoopQueue, peri, len);

    return stat;
#else
    uint16_t tail_len = 0;

    if(!LoopQueue_IsOverFlow(&LoopQueue, len)) {
        if(LoopQueue.writeIndex + len <= LOOP_QUEUE_MAX_SIZE) {
            LoopQueue_WriteFnCB(peri, &(LoopQueue.buf[LoopQueue.writeIndex]), len);
            LoopQueue.writeIndex += len;
        } else {
            tail_len = LOOP_QUEUE_MAX_SIZE - LoopQueue.writeIndex;
            LoopQueue_WriteFnCB(peri, &(LoopQueue.buf[LoopQueue.writeIndex]), tail_len);

            LoopQueue.writeIndex = 0;
            LoopQueue_WriteFnCB(peri, &(LoopQueue.buf[LoopQueue.writeIndex]), len - tail_len);
            LoopQueue.writeIndex += (len - tail_len);
        }
        LoopQueue.writeIndex &= QUEUE_CAPABILITY;
    } else {
        /* Discard data */
        uint8_t discard_buf[LOOP_QUEUE_DISCARD_MAX_SIZE];

        len = (len > LOOP_QUEUE_DISCARD_MAX_SIZE) ? LOOP_QUEUE_DISCARD_MAX_SIZE : len;
        LoopQueue_WriteFnCB(peri, discard_buf, len);
        DATATRANS_QUEUE_BUFFER(MODULE_DRIVERTASK, LEVEL_ERROR, "Discard bytes: %d!", 1, len);

        return FALSE;
    }

    return TRUE;
#endif
}

/**
  * @brief  Get valid data length.
  * @param None.
  * @retval loop queue length.
  */
uint16_t DataTrans_GetFIFOLen(void)
{
    return LoopQueue_GetDataLen(&LoopQueue);
}

/**
  * @brief  Read data from loop queue.
  * @param None.
  * @retval None.
  */
void DataTrans_ReadQueue(uint8_t * pBuf, uint16_t len)
{
    LoopQueue_ReadBuf(&LoopQueue, pBuf, len);
}

/**
  * @brief  copy buffer data from loop queue.
  * @param None.
  * @retval None.
  */
void DataTrans_CopyBuf(uint8_t * pBuf, uint16_t len)
{
    LoopQueue_CopyData(&LoopQueue, pBuf, len);
}

/**
  * @brief  Find special data in loop queue.
  * @param data: Find data.
  * @param data: data length in Loop queue.
  * @retval actual data position in loop queue.
    0: no matching data found.
    1: first index in loop queue and so on.
  */
uint16_t DataTrans_FindData(uint8_t data, uint16_t len)
{
    uint16_t pos = LoopQueue.readIndex;

    while(len--) {
        pos &= QUEUE_CAPABILITY;

        if(data == LoopQueue.buf[pos++]) {
            return (pos + LOOP_QUEUE_MAX_SIZE - LoopQueue.readIndex) & QUEUE_CAPABILITY;
        }
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////


#define RBUF_DEFAULT_SIZE 8192

#if RX_RBUF_RWSYNC_ENABLE == 1

u8 RxWaitUsedFlag = INVALID_FLAG_ID;
u8 RxWaitAvailableFlag = INVALID_FLAG_ID;
bool bRxWaitUsed = FALSE;
bool bRxWaitAvailable = FALSE;

#endif

#if TX_RBUF_RWSYNC_ENABLE == 1

u8 TxWaitUsedFlag = INVALID_FLAG_ID;
u8 TxWaitAvailableFlag = INVALID_FLAG_ID;
bool bTxWaitUsed = FALSE;
bool bTxWaitAvailable = FALSE;

#endif

rbuf_t * bbpro_rbuf_create(int size)
{
    rbuf_t * new_rb;
    new_rb = (rbuf_t *)malloc(sizeof(rbuf_t));
    if(!new_rb) {
        /* TODO - Error Messaeggs */
        return NULL;
    }
    memset((void *)new_rb, 0, sizeof(rbuf_t));
    if(size == 0)
        new_rb->size = RBUF_DEFAULT_SIZE;
    else
        new_rb->size = size;
    new_rb->buf = (uint8_t *)malloc(new_rb->size);
    if(!new_rb->buf) {
        /* TODO - Error Messaeggs */
        free(new_rb);
        return NULL;
    }

    return new_rb;
}

void bbpro_rbuf_set_mode(rbuf_t * rbuf, rbuf_mode_t mode)
{
    rbuf->mode = mode;
}

rbuf_mode_t bbpro_rbuf_mode(rbuf_t * rbuf)
{
    return (rbuf_mode_t)rbuf->mode;
}

int bbpro_rbuf_read(rbuf_t * rb, uint8_t * out, int size)
{

    int read_size = size > rb->used ? rb->used : size;
    int to_end = rb->size - rb->rfx;
    int start_size;

    if(read_size > to_end) {  // check if we need to wrap around
        memcpy(out, &rb->buf[rb->rfx], to_end);
        start_size = read_size - to_end;
        memcpy(out + to_end, &rb->buf[0], start_size);
        rb->rfx = start_size;
    } else {
        memcpy(out, &rb->buf[rb->rfx], read_size);
        rb->rfx += read_size;
    }
    rb->used -= read_size;

    return read_size;
}

int bbpro_rbuf_write(rbuf_t * rb, uint8_t * in, int size)
{

    int available_size;
    int to_end;
    int write_size;
    int diff;
    int from_start;

    if(!rb || !in || !size) // safety belt
        return 0;

    available_size = rb->size - rb->used;
    to_end = rb->size - rb->wfx;
    write_size = (size > available_size) ? available_size : size;
    if(write_size < size && rb->mode == RBUF_MODE_OVERWRITE) {
        if(size > rb->size) {
            // the provided buffer is bigger than the
            // ringbuffer itself. Since we are in overwrite mode,
            // only the last chunk will be actually stored.
            write_size = rb->size;
            in = in + (size - write_size);
            rb->rfx = 0;
            rb->wfx = 0;
            memcpy(rb->buf, in, write_size);
            rb->used = write_size;
            // NOTE: we still tell the caller we have written all the data
            // even if the initial part has been thrown away
            return size;
        }
        // we are in overwrite mode, so let's make some space
        // for the new data by advancing the read offset
        diff = size - write_size;
        rb->rfx += diff;
        write_size += diff;
        if(rb->rfx >= rb->size)
            rb->rfx -= rb->size;
        rb->used -= diff;
    }


    if(write_size > to_end) {
        memcpy(&rb->buf[rb->wfx], in, to_end);
        from_start = write_size - to_end;
        memcpy(&rb->buf[0], in + to_end, from_start);
        rb->wfx = from_start;
    } else {
        memcpy(&rb->buf[rb->wfx], in, write_size);
        rb->wfx += write_size;
    }
    rb->used += write_size;

    return write_size;
}

int bbpro_rbuf_used(rbuf_t * rb)
{
    return rb->used;
}

int bbpro_rbuf_size(rbuf_t * rb)
{
    return rb->size;
}

int bbpro_rbuf_available(rbuf_t * rb)
{
    return rb->size - rb->used;
}

void bbpro_rbuf_clear(rbuf_t * rb)
{
    rb->rfx = rb->wfx = 0;
    rb->used = 0;
}

void bbpro_rbuf_destroy(rbuf_t * rb)
{
    free(rb->buf);
    free(rb);
}

int bbpro_rbuf_find(rbuf_t * rb, uint8_t octet)
{
    int i;
    int to_read = bbpro_rbuf_used(rb);
    if(to_read == 0)
        return -1;

    if(rb->wfx > rb->rfx) {
        for(i = rb->rfx; i <= rb->wfx; i++) {
            if(rb->buf[i] == octet)
                return(i - rb->rfx);
        }
    } else {
        for(i = rb->rfx; i < rb->size; i++) {
            if(rb->buf[i] == octet)
                return(i - rb->rfx);
        }
        for(i = 0; i <= rb->wfx; i++) {
            if(rb->buf[i] == octet)
                return((rb->size - rb->rfx) + i);
        }
    }
    return -1;
}

int bbpro_rbuf_read_until(rbuf_t * rb, uint8_t octet, uint8_t * out, int maxsize)
{
    int i;
    int j = 0;
    int size = rb->used;
    int to_read = size;
    int found = 0;
    for(i = rb->rfx; i < rb->size && (size - to_read) < maxsize; i++, j++) {
        to_read--;
        out[j] = rb->buf[i];
        if(rb->buf[i] == octet)  {
            found = 1;
            break;
        }
    }
    if(!found) {
        for(i = 0; to_read > 0 && (size - to_read) < maxsize; i++, j++) {
            to_read--;
            out[j] = rb->buf[i];
            if(rb->buf[i] == octet) {
                break;
            }
        }
    }
    bbpro_rbuf_skip(rb, (size - to_read));
    return (size - to_read);
}

static int bbpro_rbuf_copy_internal(rbuf_t * src, rbuf_t * dst, int len, int move)
{
    int to_copy;
    int available;
    int contiguous;
    int to_end;
    int remainder;
    int diff;

    if(!src || !dst || !len)
        return 0;

    to_copy = bbpro_rbuf_available(dst);
    if(len < to_copy)
        to_copy = len;

    available = bbpro_rbuf_used(src);
    if(available < to_copy)
        to_copy = available;

    contiguous = (dst->wfx > dst->rfx)
                 ? dst->size - dst->wfx
                 : dst->rfx - dst->wfx;

    if(contiguous >= to_copy) {
        if(move) {
            bbpro_rbuf_read(src, &dst->buf[dst->wfx], to_copy);
        } else {
            if(src->rfx < src->wfx) {
                memcpy(&dst->buf[dst->wfx], &src->buf[src->rfx], to_copy);
            } else {
                to_end = src->size - src->rfx;
                memcpy(&dst->buf[dst->wfx], &src->buf[src->rfx], to_end);
                dst->wfx += to_end;
                memcpy(&dst->buf[dst->wfx], &src->buf[0], to_copy - to_end);
            }
        }
        dst->wfx += to_copy;
    } else {
        remainder = to_copy - contiguous;
        if(move) {
            bbpro_rbuf_read(src, &dst->buf[dst->wfx], contiguous);
            bbpro_rbuf_read(src, &dst->buf[0], remainder);
        } else {
            if(src->rfx < src->wfx) {
                memcpy(&dst->buf[dst->wfx], &src->buf[src->rfx], contiguous);
                memcpy(&dst->buf[0], &src->buf[src->rfx + contiguous], remainder);
            } else {
                to_end = src->size - src->rfx;
                if(to_end > contiguous) {
                    memcpy(&dst->buf[dst->wfx], &src->buf[dst->rfx], contiguous);
                    diff = to_end - contiguous;
                    if(diff > remainder) {
                        memcpy(&dst->buf[0], &src->buf[dst->rfx + contiguous], remainder);
                    } else {
                        memcpy(&dst->buf[0], &src->buf[dst->rfx + contiguous], diff);
                        memcpy(&dst->buf[diff], &src->buf[0], remainder - diff);
                    }
                } else {
                    memcpy(&dst->buf[dst->wfx], &src->buf[dst->rfx], to_end);
                    diff = contiguous - to_end;
                    if(diff) {
                        memcpy(&dst->buf[dst->wfx + to_end], &src->buf[0], diff);
                        memcpy(&dst->buf[0], &src->buf[diff], remainder);
                    }
                }
            }
        }
        dst->wfx = remainder;
    }
    dst->used = to_copy;
    return to_copy;
}

int bbpro_rbuf_move(rbuf_t * src, rbuf_t * dst, int len)
{
    return bbpro_rbuf_copy_internal(src, dst, len, 1);
}

int bbpro_rbuf_copy(rbuf_t * src, rbuf_t * dst, int len)
{
    return bbpro_rbuf_copy_internal(src, dst, len, 0);
}


#endif/*USE_LV_BLUETOOTH*/

/******************* (C) COPYRIGHT 2017 Realtek Semiconductor Corporation *****END OF FILE****/

